Skip to content

Build/Test Tools: Propagate and retry the local environment commands - #12735

Open
adimoldovan wants to merge 16 commits into
WordPress:trunkfrom
adimoldovan:fix-local-env-discarded-exit-status
Open

Build/Test Tools: Propagate and retry the local environment commands#12735
adimoldovan wants to merge 16 commits into
WordPress:trunkfrom
adimoldovan:fix-local-env-discarded-exit-status

Conversation

@adimoldovan

@adimoldovan adimoldovan commented Jul 28, 2026

Copy link
Copy Markdown

Trac ticket: https://core.trac.wordpress.org/ticket/65745

start.js and docker.js propagate the exit status of the Docker Compose command they run, falling back to 1 when status is null. docker.js treats SIGINT as cancellation rather than failure. start.js stops before composer update -W when the containers did not come up.

compose_with_retry() in utils.js re-attempts a command three times, 10 and 20 seconds apart. It covers docker compose pull and docker compose up, which reach Docker Hub, and composer install and composer update, which reach repo.packagist.org. Every other command runs once, including typecheck:php and typecheck:php:baselines. The loop stops early when the command was killed by a signal or could not be spawned.

ensure_env_file() in utils.js creates .env from .env.example, resolved against the repository root. start.js, docker.js and install.js all call it, so npm run env:pull no longer resolves image tags without it.

Backport

affected branches why
discarded exit status 6.8, 6.9, 7.0, 7.1 6.7 and earlier call execSync, which throws
asynchronous .env copy 6.7, 6.8, 7.0 6.9 and 7.1 already use copyFileSync
no retry all branches with the scripts new behaviour, not a defect

Testing instructions

  1. The exit status of a failed start. Before this change it is 0. After it, 1, after three attempts (~33s).

    LOCAL_PHP=this-tag-does-not-exist node ./tools/local-env/scripts/start.js; echo $?
    
  2. The exit status when Docker cannot be run. Before this change it is 0, reported after 30s of retries. After it, 1, reported immediately.

    env PATH=/var/empty $(which node) ./tools/local-env/scripts/docker.js pull; echo $?
    
  3. The pull retry. Three attempts, 10 and 20 seconds apart, ending with docker compose pull failed after 3 attempts. and exit 1.

    LOCAL_PHP=this-tag-does-not-exist npm run env:pull; echo $?
    
  4. Commands that are not re-attempted run once. The first exits 1 in about a second with no retry message.

    npm run env:composer -- nonexistent-script; echo $?
    npm run env:logs
    

    Ctrl-C ends env:logs and does not restart it.

  5. .env is created before the image tags are resolved. Delete .env, then run the pull and confirm .env exists afterwards. Before this change only npm run env:start created it.

    rm .env && npm run env:pull && ls .env
    
  6. A cold start still works.

    npm run env:stop && docker compose down -v
    npm run env:start && npm run env:install
    

Use of AI Tools

AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus 5
Used for: Investigating the CI failures, writing the patch, and iterative code review. Every behaviour and branch range described above was verified locally.


This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.

…commands.

`start.js` runs `docker compose up` through `spawnSync` and never inspects the
result, so a failed pull does not fail the script. Execution continues into
`composer update -W` with containers that may not exist, and the error surfaces
later and in the wrong place.

`docker.js` calls `process.exit( returns.status )`, and `status` is `null` when
Docker cannot be spawned. `process.exit( null )` exits 0, so `npm run env:pull`
reports success when the Docker CLI is missing.

Reproduce with:

    LOCAL_PHP=this-tag-does-not-exist npm run env:start; echo $?

See #65745.
Copilot AI review requested due to automatic review settings July 28, 2026 14:15
@adimoldovan adimoldovan self-assigned this Jul 28, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the local Docker environment helper scripts to correctly propagate failures from spawnSync() so npm commands fail at the correct step (instead of continuing after Docker failures and surfacing errors later).

Changes:

  • Capture and check the result of docker compose up in start.js, exiting non-zero when container startup fails.
  • Avoid exiting 0 when the Docker CLI cannot be spawned in docker.js by handling status === null and reporting a clearer error.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
tools/local-env/scripts/start.js Capture spawnSync() result for docker compose up and exit non-zero on failure to start/pull containers.
tools/local-env/scripts/docker.js Emit an error when Docker cannot be spawned and ensure a non-zero exit code when appropriate.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tools/local-env/scripts/docker.js Outdated
@github-actions

Copy link
Copy Markdown

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

…nment command.

`docker.js` exited 0 for any signal, so a command killed by SIGTERM or SIGKILL reported success. That reintroduced the false success this ticket set out to remove: the retry loop in `reusable-phpunit-tests-v3.yml` branches on the status of `npm run env:pull` and would treat a killed pull as a completed one.

Restrict the exemption to SIGINT, which is how a long-running command such as `env:logs` is normally ended. Report every other signal and exit non-zero.

`start.js` exempts no signal, because `env:start` runs `composer update -W` next and that must not run against containers that never came up. Say so in the comment, so the difference between the two files is deliberate.

Also fold the unreachable `up.error` branch in `start.js` into the failure message. The `docker info` check above it already throws when the Docker CLI is missing or the daemon is down.
Copilot AI review requested due to automatic review settings July 28, 2026 14:50

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

@adimoldovan
adimoldovan marked this pull request as ready for review July 28, 2026 15:30
@adimoldovan
adimoldovan requested a review from lancewillett July 28, 2026 15:30
@github-actions

github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props adrianmoldovanwp, lancewillett, johnbillion.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@lancewillett lancewillett left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested with head a88479c

Confirmed failed image pulls and a missing Docker executable exit non-zero. SIGTERM exits non-zero, SIGINT remains clean for interactive commands, and a normal environment start and install > succeeded

The affected code shape was also verified on 6.8, 6.9, and 7.0.

Good to land.

Copilot AI review requested due to automatic review settings July 29, 2026 21:12

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

@lancewillett

Copy link
Copy Markdown
Member

This change looks correct, I'd like a 2nd review from a core committer on the backports since that will involve SVN commits to 7.0, 6.9, and 6.8. CC @aaronjorbin @desrosj @johnbillion

Copilot AI review requested due to automatic review settings July 30, 2026 09:49

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

…egistry.

Moves the retry loop from [63163] into `utils.js` so `start.js` can use it too, and
adds `ensure_env_file()` so every script creates `.env` before Compose reads it.

`docker.js` retries `pull` and any command containing `composer`. `start.js` retries
`docker compose up`.

Folds in the changes from PR WordPress#12736.

See #65745.
@adimoldovan adimoldovan changed the title Build/Test Tools: Propagate the exit status of the local environment commands Build/Test Tools: Propagate and retry the local environment commands Aug 13, 2026
@adimoldovan

Copy link
Copy Markdown
Author

Tested with head a88479c

Confirmed failed image pulls and a missing Docker executable exit non-zero. SIGTERM exits non-zero, SIGINT remains clean for interactive commands, and a normal environment start and install > succeeded

The affected code shape was also verified on 6.8, 6.9, and 7.0.

Good to land.

@lancewillett this needs a new round of review, I just folded #12736 into it.

…mpted.

Stop the retry loop when the command could not be spawned at all. That error fails
the same way every time, so re-attempting it only delays the report by 30 seconds.

Re-attempt `composer install` and `composer update`, rather than every Composer run.
`typecheck:php` and `typecheck:php:baselines` reach no registry, so a PHPStan failure
is a real result and was being reported three times.

See #65745.
Comment thread tools/local-env/scripts/docker.js Outdated
Composer accepts global options before the subcommand, so `env:composer -- -n update`
placed `-n` where the subcommand was expected and lost the retry.

See #65745.
Global options can precede the command, and `--working-dir` takes a separate value, so
searching the arguments matched tokens that were never the command. Both
`env:composer -- --working-dir update validate` and `typecheck:php -- update` were
re-attempted three times despite reaching no registry.

See #65745.
@lancewillett

Copy link
Copy Markdown
Member

Re-reviewed and tested head 87734e3 after the Composer option-parsing follow-up and trunk refresh.

Confirmed:

  • Missing Docker exits non-zero without retrying
  • Failed pulls and starts retry three times, then exit non-zero
  • Non-registry Composer commands run once
  • .env is created before Compose runs
  • A cold start and install succeed

No blocking issues. Looks good to land.

pento pushed a commit that referenced this pull request Aug 31, 2026
`start.js` and `docker.js` can report success when Docker Compose fails to start, exits unsuccessfully, or is killed. Propagate failures while preserving SIGINT as cancellation for interactive commands.

Create `.env` synchronously before each local environment script loads it. This prevents Docker Compose from resolving image tags before the configuration exists.

Developed in: #12735

Props adrianmoldovanwp.
See #65745.

git-svn-id: https://develop.svn.wordpress.org/trunk@63416 602fd350-edb4-49c9-b593-d223f7449a82
markjaquith pushed a commit to markjaquith/WordPress that referenced this pull request Aug 31, 2026
`start.js` and `docker.js` can report success when Docker Compose fails to start, exits unsuccessfully, or is killed. Propagate failures while preserving SIGINT as cancellation for interactive commands.

Create `.env` synchronously before each local environment script loads it. This prevents Docker Compose from resolving image tags before the configuration exists.

Developed in: WordPress/wordpress-develop#12735

Props adrianmoldovanwp.
See #65745.
Built from https://develop.svn.wordpress.org/trunk@63416


git-svn-id: http://core.svn.wordpress.org/trunk@62608 1a063a9b-81f0-0310-95a4-ce76da25c4cd
pento pushed a commit that referenced this pull request Aug 31, 2026
Docker Hub and Packagist failures can interrupt image pulls, container startup, and Composer dependency operations. Retry those commands up to three times, waiting 10 and 20 seconds between attempts.

Stop immediately when a command is killed by a signal or cannot start. Run Composer commands that do not contact a registry once.

Developed in: #12735

Props adrianmoldovanwp.
See #65745.

git-svn-id: https://develop.svn.wordpress.org/trunk@63417 602fd350-edb4-49c9-b593-d223f7449a82
markjaquith pushed a commit to markjaquith/WordPress that referenced this pull request Aug 31, 2026
Docker Hub and Packagist failures can interrupt image pulls, container startup, and Composer dependency operations. Retry those commands up to three times, waiting 10 and 20 seconds between attempts.

Stop immediately when a command is killed by a signal or cannot start. Run Composer commands that do not contact a registry once.

Developed in: WordPress/wordpress-develop#12735

Props adrianmoldovanwp.
See #65745.
Built from https://develop.svn.wordpress.org/trunk@63417


git-svn-id: http://core.svn.wordpress.org/trunk@62609 1a063a9b-81f0-0310-95a4-ce76da25c4cd
@lancewillett

Copy link
Copy Markdown
Member

https://core.trac.wordpress.org/changeset/63416 and https://core.trac.wordpress.org/changeset/63417 for trunk

I'll proceed to backport r63416 to older branches once I have a 2nd core committer review.

@lancewillett

Copy link
Copy Markdown
Member

Trunk landed in two revisions:

  • r63416 contains the backportable bug fixes.
  • r63417 adds retry behavior and will remain on trunk because it is an enhancement rather than a defect fix.

For the r63416 backports, the planned scope is:

Branch Changes
7.1 Exit-status fix
7.0 Exit-status and .env fixes
6.9 Exit-status fix
6.8 Exit-status and .env fixes
6.7 Optional .env fix only

The adapted backports will record mergeinfo for r63416. I’m keeping Trac #65745 open until the agreed backports land.

@johnbillion

Copy link
Copy Markdown
Member

Planned scope back to 6.7 looks good. Let's do this via a PR for each branch though so anything unexpected in CI gets caught.

@adimoldovan

Copy link
Copy Markdown
Author

Planned scope back to 6.7 looks good. Let's do this via a PR for each branch though so anything unexpected in CI gets caught.

Opened backport PRs:

PR Base Backports
#13350 6.7 r62871
#13353 6.8 r63416 + r62871
#13351 6.9 r63416
#13352 7.0 r63416 + r62871
#13354 7.1 r63416

6.7 has no exit-status defect: docker.js there uses execSync, which throws. 6.9 and 7.1 already carry
r62871, so they take only the exit-status half of r63416.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In review

Development

Successfully merging this pull request may close these issues.

4 participants